package com.bill99.rpc.registry.url;

import java.util.HashMap;
import java.util.Map;

import com.bill99.rpc.registry.BaseUrl;

/**
 * 
 * @author sheng.zhang
 * 
 */
public class ReferenceUrl extends BaseUrl {

	private static final long serialVersionUID = 1L;

	// ==== cache ====

	private volatile transient String string;

	private volatile transient String full;

	public ReferenceUrl(String protocol, String host, int port, String path,
			Map<String, String> parameters) {
		super(protocol, host, port, path, parameters);
	}

	public static ReferenceUrl valueOf(String referenceUrl) {
		if (referenceUrl == null || referenceUrl.trim().length() == 0) {
			throw new IllegalArgumentException("referenceUrl == null");
		}
		String protocol = null;
		String host = null;
		int port = 0;
		String path = null;
		Map<String, String> parameters = null;
		int i = referenceUrl.indexOf("?");
		if (i >= 0) {
			String[] parts = referenceUrl.substring(i + 1).split("\\&");
			parameters = new HashMap<String, String>();
			for (String part : parts) {
				part = part.trim();
				if (part.length() > 0) {
					int j = part.indexOf('=');
					if (j >= 0) {
						parameters.put(part.substring(0, j),
								part.substring(j + 1));
					} else {
						parameters.put(part, part);
					}
				}
			}
			referenceUrl = referenceUrl.substring(0, i);
		}
		i = referenceUrl.indexOf("://");
		if (i == 0) {
			throw new IllegalStateException("referenceUrl missing protocol: \""
					+ referenceUrl + "\"");
		} else if (i > 0) {
			protocol = referenceUrl.substring(0, i);
			referenceUrl = referenceUrl.substring(i + 3);
		}

		i = referenceUrl.indexOf("/");
		if (i >= 0) {
			path = referenceUrl.substring(i + 1);
			referenceUrl = referenceUrl.substring(0, i);
		}
		i = referenceUrl.indexOf(":");
		if (i >= 0 && i < referenceUrl.length() - 1) {
			port = Integer.parseInt(referenceUrl.substring(i + 1));
			referenceUrl = referenceUrl.substring(0, i);
		}
		if (referenceUrl.length() > 0)
			host = referenceUrl;
		return new ReferenceUrl(protocol, host, port, path, parameters);
	}

	public ReferenceUrl addParameters(Map<String, String> parameters) {
		if (parameters == null || parameters.isEmpty()) {
			return this;
		}
		boolean hasAndEqual = true;
		for (Map.Entry<String, String> entry : parameters.entrySet()) {
			String value = getParameters().get(entry.getKey());
			if (value == null && entry.getValue() != null
					|| !value.equals(entry.getValue())) {
				hasAndEqual = false;
				break;
			}
		}
		// 如果没有修改，直接返回。
		if (hasAndEqual)
			return this;

		Map<String, String> map = new HashMap<String, String>(getParameters());
		map.putAll(parameters);
		return new ReferenceUrl(protocol, host, port, path, map);
	}

	public ReferenceUrl removeParameters(String... keys) {
		if (keys == null || keys.length == 0) {
			return this;
		}
		Map<String, String> map = new HashMap<String, String>(getParameters());
		for (String key : keys) {
			map.remove(key);
		}
		if (map.size() == getParameters().size()) {
			return this;
		}
		return new ReferenceUrl(protocol, host, port, path, map);
	}

	private String buildString() {
		StringBuilder buf = new StringBuilder();
		if (protocol != null && protocol.length() > 0) {
			buf.append(protocol);
			buf.append("://");
		}
		if (host != null && host.length() > 0) {
			buf.append(host);
			if (port > 0) {
				buf.append(":");
				buf.append(port);
			}
		}
		buildParameters(buf);
		return buf.toString();
	}

	public String toFullString() {
		if (full != null)
			return full;
		return full = buildString();
	}

	@Override
	public String toString() {
		if (string != null) {
			return string;
		}
		return string = buildString();
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((host == null) ? 0 : host.hashCode());
		result = prime * result
				+ ((parameters == null) ? 0 : parameters.hashCode());
		result = prime * result + port;
		result = prime * result
				+ ((protocol == null) ? 0 : protocol.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ReferenceUrl other = (ReferenceUrl) obj;
		if (host == null) {
			if (other.host != null)
				return false;
		} else if (!host.equals(other.host))
			return false;
		if (parameters == null) {
			if (other.parameters != null)
				return false;
		} else if (!parameters.equals(other.parameters))
			return false;
		if (port != other.port)
			return false;
		if (protocol == null) {
			if (other.protocol != null)
				return false;
		} else if (!protocol.equals(other.protocol))
			return false;
		return true;
	}

}
